/***
* ==++==
*
* Copyright (c) Microsoft Corporation.  All rights reserved.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* msg_ptr.h
*
* C++ Actors: Definition of a C++ smart pointer specifically intended for message-passing.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/

#pragma once

#include "actor_serialization.h"

namespace actors
{
    /// <summary>
    /// msg_ptr is used to efficiently pass large message payloads using a single pointer-sized
    /// value within a given address space. When passed as an argument, importantly to 'Send,' the
    /// msg_ptr copy constructor will erase the pointer from the source and move it to the target.
    /// This effectively implements 'ownership transfer' as a safe in-memory messaging strategy.
    /// </summary>
    /// <remarks>It may be that we should be using unique_ptr instead.</remarks>
    template<typename T>
    class msg_ptr
    {
    public:
        msg_ptr() : m_objectPtr(nullptr) { }

        msg_ptr(T *ptr) : m_objectPtr(ptr) { }

        msg_ptr(const msg_ptr<T> &other)  : m_objectPtr(nullptr) { _Swap(const_cast<msg_ptr<T> &>(other).m_objectPtr); }
        msg_ptr(msg_ptr<T> &&other) : m_objectPtr(nullptr) { _Swap(other.m_objectPtr); }

        msg_ptr<T>& operator=(msg_ptr<T>&& other)
            {	// construct msg_ptr object that takes resource from _Right
            _Swap(const_cast<msg_ptr<T> &>(other).m_objectPtr);
            return (*this);
            }

        msg_ptr<T>& operator=(const msg_ptr<T>& other)
            {	// assign shared ownership of resource owned by _Right
            _Swap(const_cast<msg_ptr<T> &>(other).m_objectPtr);
            return (*this);
            }

        void swap(msg_ptr<T> &other)
            {    // swap pointers
            this->_Swap(other.m_objectPtr);
            }

        void reset() { _Reset(); }

        T *get() const
            {	// return pointer to resource
            T* result = this->m_objectPtr;
            this->m_objectPtr = nullptr;
            return result;
            }

        typename T & operator*() const
            {	// return reference to resource
            return (*this->m_objectPtr);
            }

        T *operator->() const
            {	// return pointer to resource
            return (this->m_objectPtr);
            }

        operator bool() const
            {	// test if shared_ptr object owns no resource
            return (this->m_objectPtr != 0);
            }

        ~msg_ptr() { _Reset(); }

    private:
        void _Reset() { if ( m_objectPtr != nullptr ) delete m_objectPtr; m_objectPtr = nullptr; }
        void _Reset(T *new_ptr) { if ( m_objectPtr != nullptr ) delete m_objectPtr; m_objectPtr = new_ptr; }
        void _Swap(T *&other) { T *tmp = m_objectPtr; m_objectPtr = other; other = tmp; }

        T *m_objectPtr;
    };

} //namespace actors

#pragma region "Serialization of Message Pointers"
template<typename Payload>
size_t calculate_size(const actors::msg_ptr<Payload> &data)
{
    return calculate_size(*data);
}

template<typename Payload>
bool serialize(const actors::msg_ptr<Payload> &ptr, actors::Serializer &serializer)
{
    const Payload &data = *ptr;
    return serialize(data, serializer);
}

template<typename Payload>
bool deserialize(actors::msg_ptr<Payload> &data, actors::Serializer &serializer)
{
    Payload *pld = new Payload();

    if ( deserialize(*pld, serializer) )
    {
        data = msg_ptr<Payload>(pld);
        return true;
    }
    return false;
}
#pragma endregion